﻿using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Threading;
using Microsoft.IdentityModel.Protocols.WSTrust;
using Microsoft.IdentityModel.Protocols.WSTrust.Bindings;
using Microsoft.IdentityModel.SecurityTokenService;

namespace Tokens
{
   public class AdfsStsClient
   {
      public static SecurityToken GetToken(
         string hostname,
         string appliesTo,
         string keyType,
         StoreLocation storeLocation,
         StoreName storeName,
         X509FindType findType,
         Object findValue)
      {
         throw new NotImplementedException();
      }

      public static SecurityToken GetTokenWsTrust2005(
         string hostname,
         string appliesTo,
         string keyType,
         string username,
         string password)
      {
         EndpointAddress address = new EndpointAddress("https://" + hostname + "/adfs/services/trust/2005/windowstransport/");
         WindowsWSTrustBinding binding = new WindowsWSTrustBinding(SecurityMode.Transport);

         bool factoryClosed = false;
         WSTrustChannelFactory channelFactory = null;
         try
         {
            channelFactory = new WSTrustChannelFactory(binding, address);
            channelFactory.TrustVersion = TrustVersion.WSTrustFeb2005;
            channelFactory.Credentials.Windows.ClientCredential.UserName = username;
            channelFactory.Credentials.Windows.ClientCredential.Password = password;

            bool channelClosed = false;
            WSTrustChannel channel = null;
            try
            {
               channel = (WSTrustChannel)channelFactory.CreateChannel();

               // Build the Request for Security Token (RST).
               RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue);
               rst.AppliesTo = new EndpointAddress(appliesTo);
               // In order for symmetric key type to work you will have to:
               // 1. Configure a "Token Decryption" certificate in ACS. The private key of the 
               //    certificate is used by ACS to decrypt SAML tokens.
               // 2. Update the ACS RP information in ADFS so that it ADFS downloads the public 
               //    certificate that it can uses to encrypt tokens for ACS.
               // Otherwise use a bearer key type which returns a bearer token.
               rst.KeyType = keyType;

               // Perform operation to receive the Request for Security Token Response (RSTR).
               RequestSecurityTokenResponse rstr = null;
               SecurityToken token = channel.Issue(rst, out rstr);

               channel.Close();
               channelClosed = true;
               channelFactory.Close();
               factoryClosed = true;
               return token;
            }
            finally
            {
               if ((null != channel) && (!channelClosed)) channel.Abort();
            }
         }
         finally
         {
            if ((null != channelFactory) && (!factoryClosed)) channelFactory.Abort();
         }
      }

      public static SecurityToken GetTokenWsTrust13(
         string hostname,
         string appliesTo,
         string keyType,
         string username,
         string password)
      {
         EndpointAddress address = new EndpointAddress("https://" + hostname + "/adfs/services/trust/13/windowstransport/");
         WindowsWSTrustBinding binding = new WindowsWSTrustBinding(SecurityMode.Transport);

         bool factoryClosed = false;
         WSTrustChannelFactory channelFactory = null;
         try
         {
            channelFactory = new WSTrustChannelFactory(binding, address);
            channelFactory.TrustVersion = TrustVersion.WSTrust13;
            channelFactory.Credentials.Windows.ClientCredential.UserName = username;
            channelFactory.Credentials.Windows.ClientCredential.Password = password;

            bool channelClosed = false;
            WSTrustChannel channel = null;
            try
            {
               channel = (WSTrustChannel)channelFactory.CreateChannel();

               // Build the Request for Security Token (RST).
               RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue);
               rst.AppliesTo = new EndpointAddress(appliesTo);
               // In order for symmetric key type to work you will have to:
               // 1. Configure a "Token Decryption" certificate in ACS. The private key of the 
               //    certificate is used by ACS to decrypt SAML tokens.
               // 2. Update the ACS RP information in ADFS so that it ADFS downloads the public 
               //    certificate that it can uses to encrypt tokens for ACS.
               // Otherwise use a bearer key type which returns a bearer token.
               rst.KeyType = keyType;

               // Perform operation to receive the Request for Security Token Response (RSTR).
               RequestSecurityTokenResponse rstr = null;
               SecurityToken token = channel.Issue(rst, out rstr);

               channel.Close();
               channelClosed = true;
               channelFactory.Close();
               factoryClosed = true;
               return token;
            }
            finally
            {
               if ((null != channel) && (!channelClosed)) channel.Abort();
            }
         }
         finally
         {
            if ((null != channelFactory) && (!factoryClosed)) channelFactory.Abort();
         }
      }

      public static SecurityToken GetToken(
         string hostname,
         string appliesTo,
         string keyType)
      {
         EndpointAddress address = new EndpointAddress("https://" + hostname + "/adfs/services/trust/2005/windowstransport/");
         WindowsWSTrustBinding binding = new WindowsWSTrustBinding(SecurityMode.Transport);

         bool factoryClosed = false;
         WSTrustChannelFactory channelFactory = null;
         try
         {
            channelFactory = new WSTrustChannelFactory(binding, address);
            channelFactory.TrustVersion = TrustVersion.WSTrustFeb2005;
            channelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Identification;

            bool channelClosed = false; ;
            WSTrustChannel channel = null;
            try
            {
               channel = (WSTrustChannel)channelFactory.CreateChannel();

               // Build the Request for Security Token (RST).
               RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue);
               rst.AppliesTo = new EndpointAddress(appliesTo);
               // In order for symmetric key type to work you will have to:
               // 1. Configure a "Token Decryption" certificate in ACS. The private key of the 
               //    certificate is used by ACS to decrypt SAML tokens.
               // 2. Update the ACS RP information in ADFS so that it ADFS downloads the public 
               //    certificate that it can uses to encrypt tokens for ACS.
               // Otherwise use a bearer key type which returns a bearer token.
               rst.KeyType = keyType;

               RequestSecurityTokenResponse rstr = null;
               SecurityToken token = channel.Issue(rst, out rstr);

               channel.Close();
               channelClosed = true;
               channelFactory.Close();
               factoryClosed = true;
               return token;
            }
            finally
            {
               if ((null != channel) && (!channelClosed)) channel.Abort();
            }
         }
         finally
         {
            if ((null != channelFactory) && (!factoryClosed)) channelFactory.Abort();
         }
      }

      public static SecurityToken GetToken(
         string hostname,
         string appliesTo,
         string keyType,
         SecurityToken token)
      {
         throw new NotImplementedException();
      }
   }
}
